/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 compound_object.cpp

	$Header: /heroes4/compound_object.cpp $

	$NoKeywords: $

 ************************************************************************/
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "compound_object.h"

#include "attackable_obstacle.h"
#include "battlefield.h"
#include "combat_object_model_cache.h"
#include "combat_object_type.h"
#include "combat_reader.h"
#include "compound_object_model.h"
#include "stationary_combat_object.h"
#include "strip_resource_name.h"

t_compound_object::t_compound_object() : t_idle_processor( 1, 20 )
{
	m_animating = true;
	m_removed = false;
}

t_compound_object::t_compound_object( t_battlefield& battlefield ) : t_idle_processor( 1, 20 )
{
	m_animating = true;
	m_removed = false;
}

// -----------------------------------------------------------------------
// class which contains several object segments
// -----------------------------------------------------------------------
t_compound_object::t_compound_object( t_combat_object_model_cache const& model_cache)
                 : t_idle_processor( 1, 20 ), m_model( model_cache.get() )
{
	m_model_name = model_cache.get_name();
	m_animating = true;
	m_removed = false;
	m_is_underlay = m_model->get_segments()[0]->is_underlay();
}

t_combat_object_type t_compound_object::get_object_type() const
{
	return k_combat_object_type_compound;
}

// -----------------------------------------------------------------------
// class which contains several object segments
// -----------------------------------------------------------------------
void t_compound_object::on_idle()
{
	int i;

	if (!m_animating)
	{ 
		suspend_idle_processing();
		return;
	}
	i = m_segments.size();
	while (i--)
	{
		m_segments[i]->increment_frame();
	}
}

// --------------------------------------------------------------------------
// get base point
// --------------------------------------------------------------------------
t_screen_point get_base_point( t_map_point_3d const& cell_offset, double scale );

// -----------------------------------------------------------------------
// class which contains several object segments
// -----------------------------------------------------------------------
void t_compound_object::place( t_battlefield& battlefield, t_map_point_2d const& point )
{
	int                           i;
	t_object_segment_array const& segments = m_model->get_segments();
	t_map_point_3d				  position( point, 0 );
	t_map_point_2d					footprint_size = m_model->get_footprint_size();
	t_stationary_combat_object*		object;

	position.height = battlefield.compute_terrain_height( position, footprint_size );
	m_position = position;
	i = segments.size();
	m_segments.reserve( i );
	while (i--)
	{
		object = new t_stationary_combat_object( battlefield, this );
		place_segment( battlefield, object );
	}
}

// -----------------------------------------------------------------------
// class which contains several object segments
// -----------------------------------------------------------------------
void t_compound_object::place_segment( t_battlefield& battlefield, 
									   t_stationary_combat_object* object )
{
	t_object_segment_array const&	segments = m_model->get_segments();
	t_object_segment*				segment;
	t_map_point_3d					segment_position;
	t_map_point_3d					cell_offset;
	t_screen_point					base_screen_point;
	t_screen_point					segment_screen_point;
	t_screen_point					expected_screen_point;
	double							scale = battlefield.get_model_scale();
	int								segment_number;

	m_removed = false;
	base_screen_point = battlefield.subcell_to_screen_point( m_position );
	segment_number = m_segments.size();
	if (segment_number >= segments.size())
		return;
	segment = segments[segment_number];
	object->set_segment( segment );
	segment_position = m_position + segment->get_segment_offset();
	cell_offset = t_map_point_3d( segment->get_segment_offset() >> k_battlefield_subcell_shift,
		                          0 );
	expected_screen_point = get_base_point( cell_offset, scale ) + base_screen_point;
	battlefield.place_object( object, segment_position );
	object->set_screen_position( expected_screen_point );
	battlefield.invalidate( *object );
	m_segments.push_back( object );

	if (segment_number == 0 && segment->get_frame_count() > 1)
	{
		set_delay( segment->get_ticks_per_frame() );
		if (m_animating && !is_active())
			resume_idle_processing();
	}
}

// -----------------------------------------------------------------------
// class which contains several object segments
// -----------------------------------------------------------------------
void t_compound_object::remove( t_battlefield& battlefield )
{
	if (m_removed)
		return;

	t_compound_object_ptr ref = this;
	int				      i;

	m_removed = true;
	i = m_segments.size();
	while (i--)
		battlefield.remove_object( m_segments[i] );
	m_segments.clear();
}

// -----------------------------------------------------------------------
// class which contains several object segments
// -----------------------------------------------------------------------
void t_compound_object::enable_animation( bool arg )
{
	if (m_animating == arg)
		return;
	m_animating = arg;
	if (m_animating == is_active())
		return;
	if (arg)
		resume_idle_processing();
	else
		suspend_idle_processing();
}

// -----------------------------------------------------------------------
// class which contains several object segments
// -----------------------------------------------------------------------
void t_compound_object::set_underlay( t_battlefield& battlefield, bool arg )
{
	int				i;

	if (m_is_underlay == arg)
		return;
	m_is_underlay = arg;
	for (i = 0; i < m_segments.size(); i++)
		battlefield.set_drawing_order( *m_segments[i] );
}

// -----------------------------------------------------------------------
// read a compound object from a stream
// -----------------------------------------------------------------------
static int const k_current_version = 0;

bool t_compound_object::read( t_combat_reader& reader )
{
	std::streambuf& stream = reader.get_stream();
	int				version = get<t_uint16>( stream );

	if (version < 0 || version > k_current_version)
		return false;
	m_animating = ( get<t_uint8>( stream ) != 0 );
	m_is_underlay = (get<t_uint8>( stream ) != 0 );
	if (!read_string16( stream, m_model_name ))
		return false;
	if (!m_model_name.empty())
	{
		t_combat_object_model_cache cache;

		cache = get_combat_object_model_cache( m_model_name, reader.get_battlefield().get_model_scale() );
		m_model_name = cache.get_name();
		m_model = cache.get();
	}
	m_segments.clear();
	::read( stream, &m_position, sizeof(m_position ));
	assert( m_removed == false );

	return true;
}

bool t_compound_object::write( t_combat_writer& writer ) const
{
	std::streambuf& stream = writer.get_stream();

	put<t_uint16>( stream, k_current_version );
	put<t_uint8>( stream, m_animating );
	put<t_uint8>( stream, m_is_underlay );

	std::string	model_name = m_model_name;
	if (!model_name.empty())
		model_name = strip_type_and_extension( model_name );

	if (!write_string16( stream, model_name ))
		return false;
	::write( stream, &m_position, sizeof(m_position ));
	return true;
}

t_map_rect_2d t_compound_object::get_cell_rect() const
{
	if (m_segments.empty())
		return t_map_rect_2d(0,0,0,0);

	t_map_rect_2d result = m_segments[0]->get_cell_rect();
	int			  i;

	for (i = 1; i < m_segments.size(); ++i)
		result = get_extent( result, m_segments[i]->get_cell_rect() );
	return result;
}
